Skip to content

P-2171 Add Solana address validation and support#25

Merged
yosriady merged 2 commits into
mainfrom
claude/fix-solana-address-validation-UOUUK
May 7, 2026
Merged

P-2171 Add Solana address validation and support#25
yosriady merged 2 commits into
mainfrom
claude/fix-solana-address-validation-UOUUK

Conversation

@yosriady
Copy link
Copy Markdown
Contributor

@yosriady yosriady commented May 7, 2026

Summary

This PR adds comprehensive Solana address validation and support to the FormoAnalytics SDK, enabling it to handle both EVM and Solana blockchain addresses. The implementation includes Base58 validation, system address detection, and chain-aware address normalization.

Key Changes

  • New Solana address validation module (src/solana/address.ts):

    • isSolanaAddress(): Validates Base58 format and length (32-44 characters)
    • getValidSolanaAddress(): Handles string and PublicKey-like objects with toBase58() method
    • isSolanaSystemAddress() / isBlockedSolanaAddress(): Identifies system programs and blocked addresses
    • Defines SOLANA_SYSTEM_ADDRESSES constant with well-known program addresses
  • New Solana types module (src/solana/types.ts):

    • SolanaCluster type for network identification
    • SOLANA_CHAIN_IDS mapping (900001-900004) to distinguish Solana networks from EVM
    • isSolanaChainId() utility for chain identification
    • SolanaPublicKey interface for type-safe PublicKey handling
  • Enhanced address validation (src/utils/address.ts):

    • New validateAddress() function with chain-aware logic:
      • When chainId is provided: strict validation (Solana-only or EVM-only)
      • When chainId is omitted: EVM-first with Solana fallback
    • Updated isBlockedAddress() to handle both EVM (zero/dead addresses) and Solana (system programs)
    • Added validateAndChecksumAddress() helper for EVM-specific validation
  • Updated FormoAnalytics integration (src/FormoAnalytics.ts):

    • Refactored validateAndChecksumAddress() to use new validateAddress() function
    • Now accepts optional chainId parameter for chain-aware validation
    • Added clarifying comments about address format handling
  • Updated EventFactory (src/lib/event/EventFactory.ts):

    • Simplified address validation to use unified validateAddress() function
    • Maintains backward compatibility with EVM-first fallback behavior
  • Comprehensive test coverage (src/__tests__/solana.test.ts):

    • Tests for Base58 validation, length constraints, and character set validation
    • Tests for PublicKey object handling and error cases
    • Tests for system address detection
    • Tests for chain ID validation
    • Integration tests for validateAddress() with both EVM and Solana addresses
    • Tests for isBlockedAddress() with both address types

Implementation Details

  • Solana addresses use Base58 encoding (Bitcoin alphabet) without the 4 characters: 0, O, I, l
  • System program addresses are blocked to prevent events from non-user accounts
  • Chain IDs use high numbers (900000+) to avoid collision with EVM chain IDs
  • Address validation is case-sensitive for Solana (Base58) and case-insensitive for EVM (with checksum normalization)
  • The implementation gracefully handles both string addresses and PublicKey objects from the Solana SDK

https://claude.ai/code/session_01TvJLuQ7jRicEnrjzPjY2ss

Address validation in identify() and connect() previously rejected any
non-EVM address because isValidAddress only matched the 0x-prefixed hex
format. This blocked Solana wallets from being tracked.

Add a Solana validator module mirroring the web SDK and a chain-aware
validateAddress helper that:
- returns checksummed EVM addresses for EVM chain IDs (or no chainId)
- returns Base58 Solana addresses as-is for Solana chain IDs
- falls back from EVM to Solana when no chainId is supplied

isBlockedAddress now also blocks Solana system program addresses while
keeping the existing EVM zero/dead address checks.
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces Solana support to the analytics SDK by adding dedicated address validation utilities, type definitions, and cluster mappings. The core address validation logic has been refactored into a unified validateAddress function that handles both EVM (checksummed) and Solana (Base58) formats, with support for strict validation via chainId. Feedback suggests several improvements: removing a redundant undefined check in the Base58 validation loop, simplifying null/undefined checks using loose equality, and streamlining logic by leveraging existing utility functions like getValidSolanaAddress and isValidAddress for better consistency.

Comment thread src/solana/address.ts Outdated
function isValidBase58String(str: string): boolean {
for (let i = 0; i < str.length; i++) {
const ch = str[i];
if (ch === undefined || !BASE58_CHAR_SET.has(ch)) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The check for ch === undefined is redundant. Since the loop condition is i < str.length, ch will always be a character from the string during iteration.

Suggested change
if (ch === undefined || !BASE58_CHAR_SET.has(ch)) {
if (!BASE58_CHAR_SET.has(ch)) {

Comment thread src/utils/address.ts
chainId?: number | null
): string | undefined {
// Explicit Solana chainId → validate ONLY as Solana
if (chainId !== undefined && chainId !== null && isSolanaChainId(chainId)) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This check can be simplified using the loose inequality operator (!= null) to check for both null and undefined. This pattern is also applicable to the check on line 110.

Suggested change
if (chainId !== undefined && chainId !== null && isSolanaChainId(chainId)) {
if (chainId != null && isSolanaChainId(chainId)) {

Comment thread src/utils/address.ts Outdated
Comment on lines +120 to +124
if (isSolanaAddress(address)) {
return getValidSolanaAddress(address) || undefined;
}

return undefined;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This logic can be simplified by directly returning the result of getValidSolanaAddress. Since getValidSolanaAddress already performs the isSolanaAddress check and handles trimming, this avoids redundant calls and improves readability.

  return getValidSolanaAddress(address) || undefined;

Comment thread src/utils/address.ts Outdated
Comment on lines +146 to +148
if (normalized.startsWith("0x") && normalized.length === 42) {
return BLOCKED_ADDRESSES.has(normalized);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Instead of manually checking the prefix and length, leverage the existing isValidAddress utility. This ensures consistency with the EVM address validation logic used elsewhere in the SDK.

Suggested change
if (normalized.startsWith("0x") && normalized.length === 42) {
return BLOCKED_ADDRESSES.has(normalized);
}
if (isValidAddress(trimmed)) {
return BLOCKED_ADDRESSES.has(normalized);
}

- Use for...of in isValidBase58String to drop the redundant
  ch === undefined branch.
- Drop the redundant isSolanaAddress check before getValidSolanaAddress
  in the no-chainId fallback (getValidSolanaAddress already validates).
- Use isValidAddress in isBlockedAddress instead of duplicating the
  0x-prefix/length check.

Kept !== undefined && !== null over != null to match the surrounding
codebase style (FormoAnalytics.ts, EventFactory.ts).
@yosriady yosriady changed the title Add Solana address validation and support P-2171 Add Solana address validation and support May 7, 2026
@linear-code
Copy link
Copy Markdown

linear-code Bot commented May 7, 2026

@yosriady yosriady merged commit 4832ebf into main May 7, 2026
9 checks passed
@yosriady yosriady deleted the claude/fix-solana-address-validation-UOUUK branch May 7, 2026 07:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants